(function() {
	
	this.ObjUtil = { };
	
	// Methods to determine the type of an object.
	ObjUtil.isObject = function(obj) { return Object.prototype.toString.call(obj) === "[object Object]"; };
	ObjUtil.isString = function(obj) { return Object.prototype.toString.call(obj) === "[object String]"; };
	ObjUtil.isNumber = function(obj) { return Object.prototype.toString.call(obj) === "[object Number]"; };
	ObjUtil.isBoolean = function(obj) { return Object.prototype.toString.call(obj) === "[object Boolean]"; };
	ObjUtil.isArray = function(obj) { return Object.prototype.toString.call(obj) === "[object Array]"; };
	ObjUtil.isFunction = function(obj) { return Object.prototype.toString.call(obj) === "[object Function]"; };
	ObjUtil.isDate = function(obj) { return Object.prototype.toString.call(obj) === "[object Date]"; };
	ObjUtil.isRegExp = function(obj) { return Object.prototype.toString.call(obj) === "[object RegExp]"; };
	
	// Allows for creating method overloads, beware of a slight overhead during method call.  
	// Also note that the only differentiator between functions is their numer of arguments.
	// Thanks to John Resig for the inspiration for this method.
	ObjUtil.addOverload = function(obj, name, func) {
	    var old = obj[name];
	    obj[name] = function() {
	        if (func.length == arguments.length) {
	            return func.apply(obj, arguments);
	        } else if (typeof old == "function") {
	            return old.apply(obj, arguments);
			}
	    };
	};
	
	// A generic comparison operator, to maintain lexicographical order strings are cast 
	// to lower case.
	ObjUtil.compareTo = function(a, b) {
		if (ObjUtil.isString(a)) { a = a.toLowerCase(); }
		if (ObjUtil.isString(b)) { b = b.toLowerCase(); }
		if (a < b) { return -1; }
		if (a > b) { return 1; }
		return 0;
	};
	
	// A generic memoizer function to help with heavy recursion.
	// Ex: var fibonacci = ObjUtil.memo(function (recur, n) { 
		// return recur(n - 1) + recur(n - 2); }, {"0":0, "1":1});
	ObjUtil.memo = function(func, seed) {
	    var cache = seed || {};
	    var shell = function(arg) {
	        if (!(arg in cache)) {
	            cache[arg] = func(shell, arg);
			}
	        return cache[arg];
	    };
	    return shell;
	};
	
	// Shameless theft of the jQuery.extend method, but it's useful to have it here as well so as
	// not to incur too many dependencies (i.e. things relying on jQuery and extensions.js)
	ObjUtil.merge = function() {
		var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
		
		if (typeof target === "boolean") {
			deep = target;
			target = arguments[1] || {};
			i = 2;
		}
		
		if (typeof target !== "object" && !jQuery.isFunction(target)) {
			target = {};
		}
		
		if (length == i) {
			target = this;
			--i;
		}

		for (; i < length; i++)
			if ((options = arguments[ i ]) != null )
				for (var name in options) {
					var src = target[ name ], copy = options[ name ];

					if (target === copy) { continue; }

					if (deep && copy && typeof copy === "object" && !copy.nodeType) {
						target[ name ] = jQuery.extend( deep, 
							src || ( copy.length != null ? [ ] : { } )
						, copy );
					} else if (copy !== undefined) {
						target[ name ] = copy;
					}
				}

		return target;
	};
	
	// Gives us partial functions with currying.  Inspired by Oliver Steele's Functional.js library.
	// Parameters to be supplied by the curried function use 'undefined' as a placeholder in the
	// defining function.  Ex: var test = someFunction(a, undefined, c) { }; test(b);
	if (!Function.partial) {
		Function.prototype.partial = function() {
			var fn = this;
			var args = Array.prototype.slice.call(arguments);
	    	return function() {
				var arg = 0;
				for (var i = 0; i < args.length && arg < arguments.length; i++) {
					if (args[i] === undefined) {
						args[i] = arguments[arg++];
					}
				}
				return fn.apply(this, args);
	    	};
		};
	}

	// Converts strings to boolean values
	if (!String.toBoolean) {
		String.prototype.toBoolean = function() { 
			return this.toLowerCase() == "true" || this == "1";
		};
	}
	
	// Replaces all instance of 'val' within a string and returns a new string.  The native 
	// String.replace method only replaces the first instance of the match.  Unfortunately 
	// you cannot assign to 'this' within a prototype so we have to return a new instance 
	// of the string.
	if (!String.replaceAll) {
		String.prototype.getReplaced = function(val, rep) {
			var s = this.replace(new RegExp(val, "g"), rep);
			return s;
		};
	}
	
	// Removes white space from the beginning and end of a string
	if (!String.trim) {
		String.prototype.trim = function () {
		    return this.replace(/^\s*/, "").replace(/\s*$/, "");
		};
	}
	
	// Copies all items within an array to a new array and returns the new array.  
	// If the array contains comlex types then the new array will contain references 
	// to the old array.
	if (!Array.copy) {
		Array.prototype.copy = function() {
			var c = [];
			this.forEach(function(i) { c.push(i); });
			return c;
		};
	}

	// Wraps a for loop around an array and passes each item in the loop to the 
	// supplied function and calls it.
	if (!Array.forEach) {
		Array.prototype.forEach = function(func) { 
			for (var i in this) { if (this.hasOwnProperty(i)) { func(this[i]); } } 
		};
	}
	
	// Returns the object matching the criteria function.  Note that the 'func' parameter 
	// here must be a function that returns a boolean.
	if (!Array.find) {
		Array.prototype.find = function(func) {
			for (var i in this) { if (func(this[i])) { return this[i]; } }
			return null;
		};
	}

	// Returns an array of objects matching the criteria function.  Note that the 'func' 
	// parameter here must be a function that returns a boolean.
	if (!Array.findAll) {
		Array.prototype.findAll = function(func) {
			var items = [];
			this.forEach(function(i) { if (!ObjUtil.isFunction(i)) { if (func(i)) { items.push(i); } } });
			return items;
		};
	}

	// Returns a boolean indicating that the item exists in the array.  If a criteria function is
	// passed in it tests each item against the function.
	if (!Array.contains) {
		Array.prototype.contains = function(clause) { 
			for (var i in this) { if (this[i] == clause) { return true; } }
			return false;
		};
	}

	// Returns a new array of distinct items from the original array.
	if (!Array.distinct) {
		Array.prototype.distinct = function() { 
			var items = [];
			this.forEach(function(i) { if (!items.contains(i)) { items.push(i); } });
			return items;
		};
	}

	// Returns the minimum item in the array including strings.  If the first item is a number it
	// is assumed that the entire array is made up of numbers, likewise for strings.
	if (!Array.min) {
		Array.prototype.min = function() { 
			if (this.length > 0) {
				if (ObjUtil.isNumber(this[0])) { //Assume its an array of numbers
					return Math.min.apply(Math, this);
				} else if (ObjUtil.isString(this[0])) {
					var temp = this.copy();
					temp.sortAscending();
					return temp[0];
				}
			}
			return null;
		};
	}

	// Returns the maximum item in the array including strings.  If the first item is a number it
	// is assumed that the entire array is made up of numbers, likewise for strings.
	if (!Array.max) {
		Array.prototype.max = function() { 
			if (this.length > 0) {
				if (ObjUtil.isNumber(this[0])) { //Assume its an array of numbers
					return Math.max.apply(Math, this);
				} else {
					var temp = this.copy();
					temp.sortDescending();
					return temp[0];
				}
			}
			return null;
		};
	}

	// Returns the first item in the array.
	if (!Array.first) {
		Array.prototype.first = function() { 
			return this.length > 0 ? this[0] : null; 
		};
	}

	// Retunrs the last item in the array;
	if (!Array.last) {
		Array.prototype.last = function() { 
			return this.length > 0 ? this[this.length - 1] : null; 
		};
	}

	// Sorts the array in ascending order using the Array.compareTo method.
	if (!Array.sortAscending) {
		Array.prototype.sortAscending = function() {
			this.sort(function(a,b) { return ObjUtil.compareTo(a, b); });
		};
	}

	// Sorts the array in descending order using the Array.compareTo method.
	if (!Array.sortDescending) {
		Array.prototype.sortDescending = function() {
			this.sort(function(a,b) { return ObjUtil.compareTo(b, a); });
		};
	}

	// Sorts the array in a random order.
	if (!Array.randomize) {
		Array.prototype.randomize = function() { 
			this.sort(function() { return 0.5 - Math.random(); }); 
		};
	}

	// Returns the length of the array or a count of the items matching a criteria function.
	if (!Array.count) {
		Array.prototype.count = function(func) {
			return func == null ? this.length : this.findAll(func).length;
		};
	}

	// Returns an array containing the delta items of two arrays.
	if (!Array.except) {
		Array.prototype.except = function(arr) {
			var items = [];
			var me = this;
			this.forEach(function(i) {
				if (!ObjUtil.isFunction(i)) {
					if (!arr.contains(i)) {	items.push(i); }
				}
			});
			arr.forEach(function(i) {
				if (!ObjUtil.isFunction(i)) {
					if (!me.contains(i)) { items.push(i); }
				}
			});
			return items;
		};
	}

	// Returns an array containing the overlapping items of two arrays.
	if (!Array.intersect) {
		Array.prototype.intersect = function(arr) {
			var items = [];
			this.forEach(function(i) {
				if (!ObjUtil.isFunction(i)) {
					if (arr.contains(i)) { items.push(i); }
				}
			});
			return items;
		};
	}

	// Returns the average of the numbers in a numeric array.
	if (!Array.average) {
		Array.prototype.average = function() { 
			return this.length > 0 ? this.sum() / this.length : null; 
		};
	}

	// Returns the sum of the numbers in a numeric array.
	if (!Array.sum) {
		Array.prototype.sum = function() {
			var sum = 0;
			this.forEach(function(i) { if (ObjUtil.isNumber(i)) { sum += i; } });
			return sum;
		};
	}

	// Returns a new array combining two others but excluding duplicates.
	if (!Array.union) {
		Array.prototype.union = function(arr) {
			var items = [];
			this.forEach(function(i) { items.push(i); });
			arr.forEach(function(i) { items.push(i); });
			return items.distinct();
		};
	}

	// Adds an item to an array but only if that item doesn't already exist in the array.
	if (!Array.safePush) {
		Array.prototype.safePush = function(value) {
			if (!this.contains(value)) { this.push(value); }
		};
	}

	// Returns a random item from the array.
	if (!Array.getRandom) {
		Array.prototype.getRandom = function() {
			return this[Math.floor(Math.random() * this.length)];
		};
	}
	
	// Inserts an item into the array at a specific index.
	if (!Array.insertAt) {
		Array.prototype.insertAt = function(pos, val) {
			if (pos > -1 && pos < this.length) { this.splice(pos, 0, val); }
			return false;
		};
	}
	
	// Removes all items from the array that match the supplied criteria function.
	if (!Array.remove) {
		Array.prototype.remove = function(func) {
			for (var i in this) {
				if (func(this[i])) {
					var b = this.slice(0, i);
					var e = this.slice(i + 1, this.length);
					this.length = 0;
					this.push.apply(this, b);
					this.push.apply(this, e);
				}
			}
		};
	}

	// Returns the index of a specific element in an array.
	if (!Array.indexOf) {
		Array.prototype.indexOf = function(val) {
			for (var i in this) { if (this[i] == val) { return i; } }
			return -1; // Indicate that the item was not found.
		};
	}
})();